package geometry;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.vividsolutions.jts.geom.*;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.io.ParseException;
import com.vividsolutions.jts.io.WKTReader;
import org.junit.Test;

import static com.google.common.base.Preconditions.checkArgument;

/**
 * gemotry之间的关系分析
 *
 * @author xingxing.dxx
 */
public class Operation {

    private static GeometryFactory geometryFactory = new GeometryFactory();

    /**
     * create a Point
     *
     * @param x
     * @param y
     * @return
     */
    public Coordinate point(double x, double y) {
        return new Coordinate(x, y);
    }


    /**
     * create a line
     *
     * @return
     */
    public LineString createLine(List<Coordinate> points) {
        Coordinate[] coords = points.toArray(new Coordinate[points.size()]);
        LineString line = geometryFactory.createLineString(coords);
        return line;
    }

    /**
     * 返回a指定距离内的多边形和多多边形
     *
     * @param a
     * @param distance
     * @return
     */
    public Geometry bufferGeo(Geometry a, double distance) {
        return a.buffer(distance);
    }

    /**
     * 返回(A)与(B)中距离最近的两个点的距离
     *
     * @param a
     * @param b
     * @return
     */
    public double distanceGeo(Geometry a, Geometry b) {
        return a.distance(b);
    }

    /**
     * 两个几何对象的交集
     *
     * @param a
     * @param b
     * @return
     */
    public Geometry intersectionGeo(Geometry a, Geometry b) {
        return a.intersection(b);
    }

    /**
     * 几何对象合并
     *
     * @param a
     * @param b
     * @return
     */
    public Geometry unionGeo(Geometry a, Geometry b) {
        return a.union(b);
    }

    /**
     * 在A几何对象中有的，但是B几何对象中没有
     *
     * @param a
     * @param b
     * @return
     */
    public Geometry differenceGeo(Geometry a, Geometry b) {
        return a.difference(b);
    }

    /**
     * 创建一个多边形
     */
    public Polygon createPolygonByWKT() throws ParseException {
        WKTReader reader = new WKTReader( geometryFactory );
        Polygon polygon = (Polygon) reader.read("POLYGON((35 101, 35 116, 25 117, 25 102, 35 101))");
        return polygon;
    }
    /**
     * create a Circle  创建一个圆，圆心(x,y) 半径RADIUS
     * @param x
     * @param y
     * @param RADIUS
     * @return
     */
    public Polygon createCircle(double x, double y, final double RADIUS){
        final int SIDES = 64;//圆上面的点个数
        Coordinate coords[] = new Coordinate[SIDES+1];
        for( int i = 0; i < SIDES; i++){
            double angle = ((double) i / (double) SIDES) * Math.PI * 2.0;
            double dx = Math.cos( angle ) * RADIUS;
            double dy = Math.sin( angle ) * RADIUS;
            coords[i] = new Coordinate( (double) x + dx, (double) y + dy );
        }
        coords[SIDES] = coords[0];
        LinearRing ring = geometryFactory.createLinearRing( coords );
        Polygon polygon = geometryFactory.createPolygon( ring, null );
        return polygon;
    }

    /**
     * 将字符串转成{@code Point}对象
     *
     * @param pointStr 形如{@code POINT (1.1,1.2)}
     * @return
     * @see Point
     */
    public  Point createPoint(String pointStr) {
        checkArgument(Objects.nonNull(pointStr), "pointStr must not be null.");
        WKTReader reader = new WKTReader(geometryFactory);
        Point point = null;
        try {
            point = (Point) reader.read(pointStr);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return point;
    }

    /**
     * 将一个经纬度转成{@code Point}对象
     *
     * @param longitude 经度
     * @param latitude  纬度
     * @return
     */
    public  Point createPoint(double longitude, double latitude) {
        return createPoint(createPointString(new WarpLatitude(longitude, latitude)));
    }

    /**
     * 将WKT标记的点转换成经纬度对象
     *
     * @param pointStr
     * @return
     */
    public WarpLatitude pointToLonLan(String pointStr) {
        Point point = createPoint(pointStr);
        return new WarpLatitude(point.getX(), point.getY());
    }

    /**
     * 将WKT字符串转换成Polygon对象
     *
     * @param areaStr
     * @return
     */
    public static Polygon createPolygon(String areaStr) {
        WKTReader reader = new WKTReader(geometryFactory);
        Polygon polygon = null;
        try {
            polygon = (Polygon) reader.read(areaStr);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return polygon;
    }

    /**
     * 根据给定的经纬度创建一个面
     *
     * @param longitude1 第一个点的经度
     * @param latitude1  第一个点的纬度
     * @param longitude2 第二个点的经度
     * @param latitude2  第二个点的纬度
     * @param longitude3 第三个点的经度
     * @param latitude3  第三个点的纬度
     * @param longlan    其他点的经纬度，必须成对出现
     * @return
     */
    public static Polygon craetePolygon(double longitude1, double latitude1, double longitude2, double latitude2, double longitude3, double latitude3, double... longlan) {
        List<WarpLatitude> latitudes = Lists.newArrayList();
        latitudes.add(new WarpLatitude(longitude1, latitude1));
        latitudes.add(new WarpLatitude(longitude2, latitude2));
        latitudes.add(new WarpLatitude(longitude3, latitude3));
        if (longlan != null && longlan.length > 0) {
            if (longlan.length % 2 != 0) {
                throw new IllegalArgumentException("其他点的经纬度必须成对出现");
            }
            int cursor = 0;
            double longitude = 0d, latitude = 0d;
            for (int i = 0; i < longlan.length; i++) {
                if (i % 2 == 0) {
                    longitude = longlan[i];
                } else {
                    latitude = longlan[i];
                }
                if (cursor == 1) {
                    latitudes.add(new WarpLatitude(longitude, latitude));
                    cursor = 0;
                } else {
                    cursor++;
                }
            }
        }
        return createPolygon(createPolygonString(latitudes));
    }

    /**
     * 将WKT标记的面转换成经纬度集合
     *
     * @param polygonStr
     * @return
     */
    public static List<WarpLatitude> polygonToLonLans(String polygonStr) {
        Polygon polygon = createPolygon(polygonStr);
        Coordinate[] coordinates = polygon.getCoordinates();
        List<WarpLatitude> warpLatitudes = Lists.newArrayListWithCapacity(coordinates.length);
        for (int i = 0; i < coordinates.length; i++) {
            Coordinate coordinate = coordinates[i];
            warpLatitudes.add(new WarpLatitude(coordinate.x, coordinate.y));
        }
        return warpLatitudes;
    }
    /**
     * create a line by WKT
     * @return
     * @throws ParseException
     */
    public LineString createLineByWKT(String params) throws ParseException{
        WKTReader reader = new WKTReader( geometryFactory );
        LineString line = (LineString) reader.read("LINESTRING("+params+")");
        return line;
    }

    /**
     * 将一个经纬度数据转换成WKT能够解读的点的字符串
     *
     * @param warpLatitude
     * @return
     */
    private String createPointString(WarpLatitude warpLatitude) {
        return "POINT (" + warpLatitude + ")";
    }


    /**
     * 将多个经纬度数据转换成一个面
     *
     * @param warpLatitudes
     * @return
     */
    private static String createPolygonString(List<WarpLatitude> warpLatitudes) {
        StringBuilder polygons = new StringBuilder("POLYGON((");
        polygons.append(Joiner.on(",").join(warpLatitudes));
        polygons.append("))");
        return polygons.toString();
    }

    private static class WarpLatitude {
        /**
         * 经度
         */
        private double longitude;
        /**
         * 纬度
         */
        private double latitude;

        public WarpLatitude(double longitude, double latitude) {
            this.longitude = longitude;
            this.latitude = latitude;
        }

        public double getLongitude() {
            return longitude;
        }

        public void setLongitude(double longitude) {
            this.longitude = longitude;
        }

        public double getLatitude() {
            return latitude;
        }

        public void setLatitude(double latitude) {
            this.latitude = latitude;
        }

        @Override
        public String toString() {
            return this.longitude + " " + this.latitude;
        }
    }

    public static void main(String[] args) {
        Operation op = new Operation();
        //创建一条线
        List<Coordinate> points1 = new ArrayList<>();
        points1.add(op.point(0, 0));
        points1.add(op.point(1, 3));
        points1.add(op.point(2, 3));
        LineString line1 = op.createLine(points1);
        //创建第二条线
        List<Coordinate> points2 = new ArrayList<>();
        points2.add(op.point(3, 0));
        points2.add(op.point(3, 3));
        points2.add(op.point(5, 6));
        LineString line2 = op.createLine(points2);


        System.out.println(op.distanceGeo(line1, line2));//out 1.0
        System.out.println(op.intersectionGeo(line1, line2));//out GEOMETRYCOLLECTION EMPTY
        System.out.println(op.unionGeo(line1, line2)); //out MULTILINESTRING ((0 0, 1 3, 2 3), (3 0, 3 3, 5 6))
        System.out.println(op.differenceGeo(line1, line2));//out LINESTRING (0 0, 1 3, 2 3)



    }

    @Test
    public void test() throws ParseException {
        Operation op = new Operation();
        List<Coordinate> points1 = new ArrayList<>();
        points1.add(op.point(30, 104));
        points1.add(op.point(34, 112));
        points1.add(op.point(28, 114));
        points1.add(op.point(22, 110));
        LineString line1 = op.createLine(points1);


        LineString line2 = op.createLineByWKT("30 104,34 112,28 114,22 110");
        //Coordinate point = op.point(12, 23);
        Point point = op.createPoint("POINT(30 105)");

        Polygon circle = op.createCircle(34.717787044270324, 99.73018996417524, 16);
        Polygon wkt = op.createPolygonByWKT();
        //System.out.println(  op.intersectionGeo(circle, wkt));
        System.out.println(op.intersectionGeo(line2,wkt));
    }
}